/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.ArrayList;

class Constian {
    static final int STRENGTH_NUM_TWO = 2;
    static final int STRENGTH_NUM_THREE = 3;
    static final int STRENGTH_NUM_FOUR = 4;
    static final int STRENGTH_NUM_FIVE = 5;
    static final int STRENGTH_NUM_SIX = 6;
    static final int LOOP_COUNT_OHS = 100;
    static final int LOOP_COUNT_TWOLY = 20;
    static final int LOOP_COUNT_OTLY = 20;
    static final int NUM_TEN = 10;
    static final int TIME_UNIT = 1000;
    static final int NUM_TWO_TH = 2000;
    static final int NUM_OOSZ = 1170;
    static final int NUM_SEVENTEEN = 17;
    static final int NUM_OZFZ = 1050;
}
class OrderedCollection<T> {

    public ArrayList<T> elms;

    OrderedCollection() {
        this.elms = new ArrayList<T>();
    }

    public void add(T elm) {
        this.elms.add(elm);
    }

    public T at(int index) {
        return this.elms.get(index);
    }

    public int size() {
        return this.elms.size();
    }

    public T removeFirst() {
        return this.elms.remove(this.elms.size() - 1);
    }

    public void remove(T elm) {
        int index = 0;
        int skipped = 0;
        for (int i = 0; i < this.elms.size(); i++) {
            T value = this.elms.get(i);
            if (value != elm) {
                this.elms.set(index, value);
                index ++;
            } else {
                skipped ++;
            }
        }
        for (int i = 0; i < skipped; i++) {
            this.elms.remove(this.elms.size() - 1);
        }
    }
}

/**
 * Strengths are used to measure the relative importance of constraints.
 * New strengths may be inserted in the strength hierarchy without
 * disrupting current constraints.  Strengths cannot be created outside
 * this class, so pointer comparison can be used for value comparison.
 */
class Strength {

    public int strengthValue;
    public String name;

    public static Strength strRequired = new Strength(0, "required");
    public static Strength strongPreferred = new Strength(1, "strongPreferred");
    public static Strength preferred = new Strength(Constian.STRENGTH_NUM_TWO, "preferred");
    public static Strength strongDefault = new Strength(Constian.STRENGTH_NUM_THREE, "strongDefault");
    public static Strength strNormal = new Strength(Constian.STRENGTH_NUM_FOUR, "normal");
    public static Strength weakDefault = new Strength(Constian.STRENGTH_NUM_FIVE, "weakDefault");
    public static Strength strWeakest = new Strength(Constian.STRENGTH_NUM_SIX, "weakest");

    Strength(int strengthValue, String name) {
        this.strengthValue = strengthValue;
        this.name = name;
    }

    public static boolean stronger(Strength s1, Strength s2) {
        return s1.strengthValue < s2.strengthValue;
    }

    public static boolean weaker(Strength s1, Strength s2) {
        return s1.strengthValue > s2.strengthValue;
    }

    public static Strength weakestOf(Strength s1, Strength s2) {
        return Strength.weaker(s1, s2) ? s1 : s2;
    }

    public static Strength strongest(Strength s1, Strength s2) {
        return Strength.stronger(s1, s2) ? s1 : s2;
    }

    public Strength nextWeaker() {
        switch (strengthValue) {
            case 0:
                return Strength.strWeakest;
            case 1:
                return Strength.weakDefault;
            case Constian.STRENGTH_NUM_TWO:
                return Strength.strNormal; //strNormal
            case Constian.STRENGTH_NUM_THREE:
                return Strength.strongDefault; //strongDefault
            case Constian.STRENGTH_NUM_FOUR:
                return Strength.preferred; //preferred
            case Constian.STRENGTH_NUM_FIVE:
                return Strength.strRequired; //strRequired
            default:
                return null;
        }
    }

}

/**
 ** An abstract class representing a system-maintainable relationship
 ** (or "constraint") between a set of variables. A constraint supplies
 ** a strength instance variable; concrete subclasses provide a means
 ** of storing the constrained variables and other information required
 ** to represent a constraint.
 **/
class Constraint {
    public Strength strength;

    Constraint(Strength strength) {
        this.strength = strength;
    }

    /**
     ** Activate this constraint and attempt to satisfy it.
     **/
    public void addConstraint() {
        this.addToGraph();
        Planner.planner.incrementalAdd(this);
    }

    /**
     ** Attempt to find a way to enforce this constraint. If successful,
     ** record the solution, perhaps modifying the current dataflow
     ** graph. Answer the constraint that this constraint overrides, if
     ** there is one, or nil, if there isn't.
     ** Assume: I am not already satisfied.
     **/
    public Constraint satisfy(int mark) {
        this.chooseMethod(mark);
        if (!this.isSatisfied()) {
            if (this.strength != Strength.strRequired) {

            }
            return null;
        }
        this.markInputs(mark);
        Variable out = this.output();
        Constraint overridden = out.determinedBy;
        if (overridden != null) {
            overridden.markUnsatisfied();
        }
        out.determinedBy = this;
        if (!Planner.planner.addPropagate(this, mark)) {
//            System.out.println("Cycle encountered");
        }
        out.mark = mark;
        return overridden;
    };

    public void destroyConstraint() {
        if (this.isSatisfied()) {
            Planner.planner.incrementalRemove(this);
        } else {
            this.removeFromGraph();
        }
    }

    /**
     * Normal constraints are not input constraints.  An input constraint
     * is one that depends on external state, such as the mouse, the
     * keybord, a clock, or some arbitraty piece of imperative code.
     */
    public boolean isInput() {
        return false;
    }
    public void markUnsatisfied() {}
    public void recalculate() {}
    public boolean isSatisfied() {
        return true;
    }
    public void removeFromGraph() {}
    public boolean inputsKnown(int mark) {
        return true;
    }
    public void execute() {}
    public void addToGraph() {}
    public void chooseMethod(int mark) {}
    public void markInputs(int mark) {}
    public Variable output() {
        return null;
    }
}

class UnaryConstraint extends Constraint {

    public Variable myOutput;
    public boolean satisfied = false;
    /**
     * Abstract superclass for constraints having a single possible output
     * variable.
     */
    UnaryConstraint(Variable v, Strength strength) {
        super(strength);
        this.myOutput = v;
        this.satisfied = false;
        this.addConstraint();
    }

    /**
     * Adds this constraint to the constraint graph
     */
    @Override
    public void addToGraph() {
        this.myOutput.addConstraint(this);
        this.satisfied = false;
    }

    /**
     * Decides if this constraint can be satisfied and records that
     * decision.
     */
    @Override
    public void chooseMethod(int mark) {
        this.satisfied = this.myOutput.mark != mark && Strength.stronger(this.strength, this.myOutput.walkStrength);
    }

    /**
     * Returns true if this constraint is satisfied in the current solution.
     */
    @Override
    public boolean isSatisfied() {
        return this.satisfied;
    }

    @Override
    public void markInputs(int mark) {}

    /**
     * Returns the current output variable.
     */
    @Override
    public Variable output() {
        return this.myOutput;
    }

    /**
     * Calculate the walkabout strength, the stay flag, and, if it is
     * 'stay', the value for the current output of this constraint. Assume
     * this constraint is satisfied.
     */
    @Override
    public void recalculate() {
        this.myOutput.walkStrength = this.strength;
        this.myOutput.stay = !this.isInput();
        if (this.myOutput.stay) {
            this.execute();
        }
    }

    /**
     * Records that this constraint is unsatisfied
     */
    @Override
    public void markUnsatisfied() {
        this.satisfied = false;
    }

    @Override
    public boolean inputsKnown(int mark) {
        return true;
    }

    @Override
    public void removeFromGraph() {
        if (this.myOutput != null) {
            this.myOutput.removeConstraint(this);
        }
        this.satisfied = false;
    }
}

/**
 * Variables that should, with some level of preference, stay the same.
 * Planners may exploit the fact that instances, if satisfied, will not
 * change their output during plan execution.  This is called "stay
 * optimization".
 */
class StayConstraint extends UnaryConstraint {

    StayConstraint(Variable v, Strength str) {
        super(v, str);
    }

    @Override
    public void execute() {

    }
}

/**
 * A unary input constraint used to mark a variable that the client
 * wishes to change.
 */
class EditConstraint extends UnaryConstraint {

    EditConstraint(Variable v, Strength str) {
        super(v, str);
    }

    /**
     * Edits indicate that a variable is to be changed by imperative code.
     */
    @Override
    public boolean isInput() {
        return true;
    }

    @Override
    public void execute() {}
}

enum Direction {
    NONE(0),
    FORWARD(1),
    BACKWARD(-1);

    private final int value;

    Direction(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

/**
 * Abstract superclass for constraints having two possible output
 * variables.
 */
class BinaryConstraint extends Constraint {

    public Variable v1;
    public Variable v2;
    public Direction direction = Direction.NONE;
    BinaryConstraint(Variable var1, Variable var2, Strength strength) {
        super(strength);
        this.v1 = var1;
        this.v2 = var2;
        this.addConstraint();
    }

    /**
     * Decides if this constraint can be satisfied and which way it
     * should flow based on the relative strength of the variables related,
     * and record that decision.
     */
    @Override
    public void chooseMethod(int mark) {
        if (this.v1.mark == mark) {
            this.direction = this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength) ? Direction.FORWARD : Direction.NONE;
        }

        if (v2.mark == mark) { // relative v2.mark
            this.direction = this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength) ? Direction.BACKWARD : Direction.NONE;
        }

        if (Strength.weaker(v1.walkStrength, v2.walkStrength)) { // weaker v1 and v2
            this.direction = Strength.stronger(this.strength, this.v1.walkStrength) ? Direction.BACKWARD : Direction.NONE;
        } else {  // stronger v1 and v2
            this.direction = Strength.stronger(this.strength, this.v2.walkStrength) ? Direction.FORWARD : Direction.BACKWARD;
        }
    }

    /***
     ** Add this constraint to the constraint graph
     **/
    @Override
    public void addToGraph() {
        this.v1.addConstraint(this);
        this.v2.addConstraint(this);
        this.direction = Direction.NONE;
    }

    /**
     * Answer true if this constraint is satisfied in the current solution.
     */
    @Override
    public boolean isSatisfied() {
        return this.direction != Direction.NONE;
    }

    /**
     * Mark the input variable with the given mark.
     */
    @Override
    public void markInputs(int mark) {
        this.input().mark = mark;
    }

    /**
     * Returns the current output variable
     */
    public Variable input() {
        return this.direction == Direction.FORWARD ? this.v1 : this.v2;
    }

    /**
     * Returns the current input variable
     */
    @Override
    public Variable output() {
        return this.direction == Direction.FORWARD ? this.v2 : this.v1;
    }

    /**
     * Calculate the walkabout strength, the stay flag, and, if it is
     * 'stay', the value for the current output of this
     * constraint. Assume this constraint is satisfied.
     */
    @Override
    public void recalculate() {
        Variable ihn = this.input();
        Variable out = this.output();
        out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
        out.stay = ihn.stay;
        if (out.stay) {
            this.execute();
        }
    }

    /**
     * Record the fact that this constraint is unsatisfied.
     */
    @Override
    public void markUnsatisfied() {
        this.direction = Direction.NONE;
    }

    @Override
    public boolean inputsKnown(int mark) {
        Variable i = this.input();
        return i.mark == mark || i.stay || i.determinedBy == null;
    }

    @Override
    public void removeFromGraph() {
        if (this.v1 != null) {
            this.v1.removeConstraint(this);
        }
        if (this.v2 != null) {
            this.v2.removeConstraint(this);
        }
        this.direction = Direction.NONE;
    }
}

/**
 * Relates two variables by the linear scaling relationship: "v2 =
 * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
 * this relationship but the scale factor and offset are considered
 * read-only.
 */
class ScaleConstraint extends BinaryConstraint {

    public Variable scale;
    public Variable offset;
    public Direction direction = Direction.NONE;

    ScaleConstraint(Variable src, Variable scale, Variable offset, Variable des, Strength strength) {
        super(src, des, strength);

        this.scale = scale;
        this.offset = offset;
        this.realAddConstraint();
    }

    @Override
    public void addConstraint() {}

    public void realAddConstraint() {
        super.addConstraint();
    }

    @Override
    public void addToGraph() {
        super.addToGraph();
        this.scale.addConstraint(this);
        this.offset.addConstraint(this);
    }

    @Override
    public void removeFromGraph() {
        super.removeFromGraph();
        if (this.scale != null) {
            this.scale.removeConstraint(this);
        }
        if (this.offset != null) {
            this.offset.removeConstraint(this);
        }
    }

    @Override
    public void markInputs(int mark) {
        super.markInputs(mark);
        this.scale.mark = mark;
        this.offset.mark = mark;
    }

    /**
     * Enforce this constraint. Assume that it is satisfied.
     */
    @Override
    public void execute() {
        if (this.direction == Direction.FORWARD) { // Direction.FORWARD
            this.v2.value = this.v1.value * this.scale.value + this.offset.value;
        } else {
            this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
        }
    }

    /**
     ** Calculate the walkabout strength, the stay flag, and, if it is
     ** 'stay', the value for the current output of this constraint. Assume
     ** this constraint is satisfied.
     **/
    @Override
    public void recalculate() {
        Variable ihn = this.input();
        Variable out = this.output();
        out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
        out.stay = ihn.stay && this.scale.stay && this.offset.stay;
        if (out.stay) {
            this.execute();
        }
    }
}

/**
 * Constrains two variables to have the same value.
 */
class EqualityConstraint extends BinaryConstraint {
    EqualityConstraint(Variable var1, Variable var2, Strength strength) {
        super(var1, var2, strength);
    }

    @Override
    public void execute() {
        this.output().value = this.input().value;
    }
}

/**
 ** A constrained variable. In addition to its value, it maintain the
 ** structure of the constraint graph, the current dataflow graph, and
 ** various parameters of interest to the DeltaBlue incremental
 ** constraint solver.
 **/
class Variable {
    public String name;
    public OrderedCollection<Constraint> constraints;
    public int value;
    public Constraint determinedBy;
    public int mark;
    public Strength walkStrength;
    public boolean stay;

    Variable(String name, Integer initialValue) {
        this.value = initialValue == null ? 0 : initialValue;
        this.constraints = new OrderedCollection<Constraint>();
        this.mark = 0;
        this.walkStrength = Strength.strWeakest;
        this.name = name;
        this.determinedBy = null;
        this.stay = true;
    }

    /**
     ** Add the given constraint to the set of all constraints that refer
     ** this variable.
     **/
    public void addConstraint(Constraint c) {
        constraints.add(c);
    }

    /**
     * Removes all traces of c from this variable.
     */
    public void removeConstraint(Constraint c) {
        constraints.remove(c);
        if (determinedBy == c) {
            determinedBy = null;
        }
    }
}

class Planner {
    public static Planner planner = new Planner();
    public int currentMark = 0;

    /**
     ** Attempt to satisfy the given constraint and, if successful,
     ** incrementally update the dataflow graph.  Details: If satifying
     ** the constraint is successful, it may override a weaker constraint
     ** on its output. The algorithm attempts to resatisfy that
     ** constraint using some other method. This process is repeated
     ** until either a) it reaches a variable that was not previously
     ** determined by any constraint or b) it reaches a constraint that
     ** is too weak to be satisfied using any of its methods. The
     ** variables of constraints that have been processed are marked with
     ** a unique mark value so that we know where we've been. This allows
     ** the algorithm to avoid getting into an infinite loop even if the
     ** constraint graph has an inadvertent cycle.
     **/
    public void incrementalAdd(Constraint c) {
        int mark = this.newMark();
        Constraint overridden = c.satisfy(mark);
        while (overridden != null) {
            overridden = overridden.satisfy(mark);
        }
    }

    /**
     ** Entry point for retracting a constraint. Remove the given
     ** constraint and incrementally update the dataflow graph.
     ** Details: Retracting the given constraint may allow some currently
     ** unsatisfiable downstream constraint to be satisfied. We therefore collect
     ** a list of unsatisfied downstream constraints and attempt to
     ** satisfy each one in turn. This list is traversed by constraint
     ** strength, strongest first, as a heuristic for avoiding
     ** unnecessarily adding and then overriding weak constraints.
     ** Assume: c is satisfied.
     **/
    public void incrementalRemove(Constraint c) {
        var out = c.output();
        c.markUnsatisfied();
        c.removeFromGraph();
        OrderedCollection<Constraint> unsatisfied = removePropagateFrom(out);
        var stren = Strength.strRequired;
        do {
            for (int i = 0; i < unsatisfied.size(); i++) {
                Constraint u = unsatisfied.at(i);
                if (u.strength == stren) {
                    this.incrementalAdd(u);
                }
            }
            stren = stren.nextWeaker();
        } while (stren != Strength.strWeakest);

    }

    /**
     ** Select a previously unused mark value.
     **/
    public int newMark() {
        return ++this.currentMark;
    }

    /**
     ** Extract a plan for resatisfaction starting from the given source
     ** constraints, usually a set of input constraints. This method
     ** assumes that stay optimization is desired; the plan will contain
     ** only constraints whose output variables are not stay. Constraints
     ** that do no computation, such as stay and edit constraints, are
     ** not included in the plan.
     ** Details: The outputs of a constraint are marked when it is added
     ** to the plan under construction. A constraint may be appended to
     ** the plan when all its input variables are known. A variable is
     ** known if either a) the variable is marked (indicating that has
     ** been computed by a constraint appearing earlier in the plan), b)
     ** the variable is 'stay' (i.e. it is a constant at plan execution
     ** time), or c) the variable is not determined by any
     ** constraint. The last provision is for past states of history
     ** variables, which are not stay but which are also not computed by
     ** any constraint.
     ** Assume: sources are all satisfied.
     **/
    public Plan makePlan(OrderedCollection<Constraint> sources) {
        int mark = this.newMark();
        Plan plan = new Plan();
        var todo = sources;
        while (todo.size() > 0) {
            Constraint constraint = todo.removeFirst();
            if ((constraint.output().mark != mark) && constraint.inputsKnown(mark)) {
                plan.addConstraint(constraint);
                constraint.output().mark = mark;
                this.addConstraintsConsumingTo(constraint.output(), todo);
            }
        }
        return plan;
    }

    /**
     ** Extract a plan for resatisfying starting from the output of the
     ** given constraints, usually a set of input constraints.
     **/
    public Plan extractPlanFromConstraints(OrderedCollection<Constraint> constraints) {
        OrderedCollection<Constraint> sources = new OrderedCollection<Constraint>();
        for (int i = 0; i < constraints.size(); i++) {
            Constraint constraint = constraints.at(i);
            if (constraint.isInput() && constraint.isSatisfied()) {
                sources.add(constraint);
            }
        }
        return this.makePlan(sources);
    }

    /**
     ** Recompute the walkabout strengths and stay flags of all variables
     ** downstream of the given constraint and recompute the actual
     ** values of all variables whose stay flag is true. If a cycle is
     ** detected, remove the given constraint and answer
     ** false. Otherwise, answer true.
     ** Details: Cycles are detected when a marked variable is
     ** encountered downstream of the given constraint. The sender is
     ** assumed to have marked the inputs of the given constraint with
     ** the given mark. Thus, encountering a marked node downstream of
     ** the output constraint means that there is a path from the
     ** constraint's output to one of its inputs.
     **/
    public boolean addPropagate(Constraint c, int mark) {
        OrderedCollection<Constraint> todo = new OrderedCollection<Constraint>();
        todo.add(c);
        while (todo.size() > 0) {
            Constraint dered = todo.removeFirst();
            if (dered.output().mark == mark) {
                this.incrementalRemove(c);
                return false;
            }
            dered.recalculate();
            this.addConstraintsConsumingTo(dered.output(), todo);
        }
        return true;
    }

    public void addConstraintsConsumingTo(Variable v, OrderedCollection<Constraint> coll) {
        Constraint determining = v.determinedBy;
        OrderedCollection<Constraint> cc = v.constraints;
        for (int i = 0; i < cc.size(); i++) {
            Constraint c = cc.at(i);
            if (c != determining && c.isSatisfied()) {
                coll.add(c);
            }
        }
    }

    /**
     * Update the walkabout strengths and stay flags of all variables
     * downstream of the given constraint. Answer a collection of
     * unsatisfied constraints sorted in order of decreasing strength.
     */
    public OrderedCollection<Constraint> removePropagateFrom(Variable out) {
        out.determinedBy = null;
        out.walkStrength = Strength.strWeakest;
        out.stay = true;
        OrderedCollection<Constraint> unsatisfied = new OrderedCollection<Constraint>();
        OrderedCollection<Variable> todo = new OrderedCollection<Variable>();
        todo.add(out);
        while (todo.size() > 0) {
            Variable v = todo.removeFirst();
            for (int i = 0; i < v.constraints.size(); i++) {
                Constraint c = v.constraints.at(i);
                if (c.isSatisfied() == false) {
                    unsatisfied.add(c);
                }
            }
            Constraint determining = v.determinedBy;
            for (int i = 0; i < v.constraints.size(); i++) {
                Constraint next = v.constraints.at(i);
                if (next != determining && next.isSatisfied()) {
                    next.recalculate();
                    todo.add(next.output());
                }
            }
        }
        return unsatisfied;
    }
}

/**
 * A Plan is an ordered list of constraints to be executed in sequence
 * to resatisfy all currently satisfiable constraints in the face of
 * one or more changing inputs.
 */
class Plan {
    public OrderedCollection<Constraint> v = new OrderedCollection<Constraint>();

    public void addConstraint(Constraint c) {
        this.v.add(c);
    }

    public int size() {
        return this.v.size();
    }

    public Constraint constraintAt(int index) {
        return this.v.at(index);
    }

    public void execute() {
        for (int i = 0; i < this.v.size(); i++) {
            Constraint c = constraintAt(i);
            c.execute();
        }
    }

}

class Deltablue {

    public void chainTest(int n) {
        Planner.planner = new Planner();
        Variable prev = null;
        Variable first = null;
        Variable last = null;
        for (int i = 0; i <= n; i++) {
            String name = "v" + i;
            Variable v = new Variable(name, 0);
            if (prev != null) {
                new EqualityConstraint(prev, v, Strength.strRequired);
            }
            if (i == 0) {
                first = v;
            }
            if (i == n) {
                last = v;
            }
            prev = v;
        }
        new StayConstraint(last, Strength.strongDefault);

        EditConstraint edit = new EditConstraint(first, Strength.preferred);
        OrderedCollection<Constraint> edits = new OrderedCollection<Constraint>();
        edits.add(edit);
        Plan plan = Planner.planner.extractPlanFromConstraints(edits);
        for (int i = 0; i < Constian.LOOP_COUNT_OHS; i++) {
            if (first != null) {
                first.value = i;
            }
//            System.out.println(first.value + "is first.value");
            plan.execute();
//            System.out.println(last.value + "is last.value");
            if (last.value != i) {
//                System.out.println("Chain test failed.");
            }
        }
    }

    public void projectionTest(int n) {
        Planner planner = new Planner();
        var scale = new Variable("scale", Constian.NUM_TEN);
        var offset = new Variable("offset", Constian.TIME_UNIT);
        Variable src = null;
        Variable dst = null;
        OrderedCollection<Variable> des = new OrderedCollection<Variable>();
        for (int i = 0; i < n; i++) {
            src = new Variable("src" + i, i);
            dst = new Variable("dst" + i, i);
            des.add(dst);
            new StayConstraint(src, Strength.strNormal);
            new ScaleConstraint(src, scale, offset, dst, Strength.strRequired);
        }

//        System.out.println("src original value is " + src.value);
//        System.out.println("dst original value is " + dst.value);
        change(src, Constian.NUM_SEVENTEEN);
//        System.out.println("src new value is " + src.value);
//        System.out.println("dst new value is " + dst.value);
        if (dst.value != Constian.NUM_OOSZ) {
//            System.out.println("Projection 1 failed");
        }

//        System.out.println("src original value is " + src.value);
//        System.out.println("dst original value is" + dst.value);
        change(dst, Constian.NUM_OZFZ);
//        System.out.println("src new value is " + src.value);
//        System.out.println("dst new value is " + dst.value);
        if (src.value != Constian.STRENGTH_NUM_FIVE) {
            ////debugMsg("Projection 2 failed")
        }
        change(scale, Constian.STRENGTH_NUM_FIVE);
        for (int i = 0; i < n - 1; i++) {
            if (des.at(i).value != i * Constian.STRENGTH_NUM_FIVE + Constian.TIME_UNIT) {
                ////debugMsg("Projection 3 failed")
            }
        }
        change(offset, Constian.NUM_TWO_TH);
        for (int i = 0; i < n - 1; i++) {
            if (des.at(i).value != i * Constian.STRENGTH_NUM_FIVE + Constian.NUM_TWO_TH) {
                ////debugMsg("Projection 4 failed")
            }
        }

    }

    public void change(Variable v, int newValue) {
        EditConstraint edit = new EditConstraint(v, Strength.preferred);
        OrderedCollection<Constraint> edits = new OrderedCollection<Constraint>();
        edits.add(edit);
        Plan plan = Planner.planner.extractPlanFromConstraints(edits);
        for (int i = 0; i < Constian.NUM_TEN; i++) {
            v.value = newValue;
            plan.execute();
        }
        edit.destroyConstraint();
    }

    public void deltaBlue() {
        chainTest(Constian.LOOP_COUNT_OHS);
        projectionTest(Constian.LOOP_COUNT_OHS);
    }
}

class Benchmark {

    public void runIteration() {
        for (int i = 0; i < Constian.LOOP_COUNT_TWOLY; i++) {
            Deltablue deltablue = new Deltablue();
            deltablue.deltaBlue();
        }
    }

    public void runIterationTime() {
        long beginTime = System.nanoTime();
        int i = 20;
        for (int j = 0; j < Constian.LOOP_COUNT_TWOLY; j++) {
            runIteration();
        }
        long endTime = System.nanoTime();
        System.out.print("deltablue: ms = " + (endTime - beginTime) / (1000.0 * 1000.0));
    }

    public static void main(String[] args) {
        Benchmark benchmark = new Benchmark();
        benchmark.runIterationTime();
    }

}
